import TableData from './tabledata';
import RowCol from './rowcol';
import CoreTable from './table';

import { each, removeClass, addClass } from '../libs/utils'

const TABLE_TYPES = ['main', 'top', 'left', 'topleft'];

class Table {
    constructor(opts = {}){
        this.width = opts.width || 800;
        this.height = opts.height || 600;

        this.fixedRowTop = opts.fixedRowTop;

        this.tableData = new TableData();
        this.colsIns = new RowCol({
            defaultSize: opts.colDefaultWidth || 40
        });
        this.rowsIns = new RowCol({
            defaultSize: opts.rowDefaultHeight || 20
        });

        let dataset = opts.dataset;

        if(dataset){
            this.tableData.setData(dataset);
            this.colsIns.setTotalCount(dataset && dataset[0] && dataset[0].length);
            this.rowsIns.setTotalCount(dataset && dataset.length);
        }

        this.coreTable = new CoreTable();

        this.$el = null;
        if(opts.el){
            this.$el = document.querySelector(opts.el);
            addClass(this.$el, 'alt-table');
        }
        this.lastPosLeft = 0;
        this.lastPosTop = 0;

        this.rendered = {
            width: 0,
            height: 0,
            info: {
                cols: [],
                rows: [],
                tds: [],
                rowStart: 0,
                rowEnd: 0,
                colStart: 0,
                colEnd: 0
            }
        }

        this.render(true, 0, 0);
    }

    _setTableRef(type, box, table){
        if(TABLE_TYPES.indexOf(type) === -1){
            console.error('不支持');
            return;
        }
        this[type + 'Table'] = {
            box: box,
            table: table
        }
    }
    _setRenderedInfo(info){
        this.rendered.info = info;
    }

    render(firstTime, scrollLeft, scrollTop){
        if(!this.$el){
            console.error('没有el');
            return;
        }
        let visibleArea = this.calcVisibleArea(firstTime, scrollLeft, scrollTop);
        let rowStart = visibleArea.rowStart;
        let rowEnd = visibleArea.rowEnd;
        let colStart = visibleArea.colStart;
        let colEnd = visibleArea.colEnd;
        console.log('render', scrollLeft, scrollTop, rowStart, rowEnd, colStart, colEnd);
        let dataset = this.tableData.getRange({
            row: rowStart,
            col: colStart
        }, {
            row: rowEnd,
            col: colEnd
        });
        let cols = this.colsIns.getData(colStart, colEnd);
        let rows = this.rowsIns.getData(rowStart, rowEnd);

        let tds = this.coreTable.createTds(dataset, rows);

        let tableDom = this.coreTable.createDom({
            tds: tds,
            cols: cols,
            rows: rows,
            tableLayout: 'auto'
        });

        this._setRenderedInfo({
            tds: tds,
            cols: cols,
            rows: rows
        })

        let left = this.colsIns.getRangeSize(0, colStart - 1);
        let top = this.rowsIns.getRangeSize(0, rowStart - 1);
        this.setTableTransform(tableDom, left, top);

        let tableBox = null;
        if(firstTime){
            tableBox = this._createTableOuterBox(tableDom, {
                boxWidth: this.width,
                boxHeight: this.height,
                totalWidth: this.colsIns.totalSize,
                totalHeight: this.rowsIns.totalSize
            });
            this.$el.appendChild(tableBox);
        }else{
            tableBox = this.mainTable.box;
            tableBox.querySelector('.alt-table-expandscroll').replaceChild(
                tableDom,
                this.mainTable.table
            );
        }
        
        this._setTableRef('main', tableBox, tableDom);
        
        this._refreshTableSize(tableDom);

        let colgroup = this.coreTable._createColGroup(cols, 'fixed');
        tableDom.replaceChild(colgroup, tableDom.querySelector('colgroup'));
        removeClass(tableDom, 'alt-auto');
        addClass(tableDom, 'alt-fixed');

        if(this.fixedRowTop){
            this.addFixedRowTopTable(firstTime, colStart, colEnd, cols);
        }

        firstTime && this.addEvents();
    }

    setTableTransform(el, left, top){
        this.lastPosLeft = left - 40;
        this.lastPosTop = top - 40;
        el.style.transform = `translate(${left - 40}px, ${top - 40}px)`;
    }

    addFixedRowTopTable(firstTime, colStart, colEnd, cols){
        let dataset = this.tableData.getRange({
            row: 0,
            col: colStart
        }, {
            row: this.fixedRowTop - 1,
            col: colEnd
        });
        let rows = this.rowsIns.getData(0, this.fixedRowTop - 1);
        let tds = this.coreTable.createTds(dataset, rows);
        let tableDom = this.coreTable.createDom({
            tds: tds,
            cols: cols,
            rows: rows,
            tableLayout: 'fixed'
        });

        let left = this.colsIns.getRangeSize(0, colStart - 1);
        let top = this.rowsIns.getRangeSize(0, -1);
        this.setTableTransform(tableDom, left, top);

        let boxHeight = this.rowsIns.getRangeSize(0, this.fixedRowTop - 1);
        let tableBox = null;
        if(firstTime){
            tableBox = this._createTableOuterBox(tableDom, {
                boxWidth: this.width,
                boxHeight: boxHeight,
                totalWidth: this.colsIns.totalSize,
                totalHeight: boxHeight
            });
            addClass(tableBox, 'top');
            this.$el.appendChild(tableBox);
        }else{
            tableBox = this.topTable.box;
            tableBox.querySelector('.alt-table-expandscroll').replaceChild(
                tableDom,
                this.topTable.table
            );
        }
        this._setTableRef('top', tableBox, tableDom);
    }

    _createTableOuterBox(table, opts = {}){
        let boxWidth = opts.boxWidth;
        let boxHeight = opts.boxHeight;
        let totalWidth = opts.totalWidth;
        let totalHeight = opts.totalHeight;

        let box = document.createElement('div');
        box.className = 'alt-table-box';
        box.style.width = `${boxWidth}px`;
        box.style.height = `${boxHeight}px`;

        let expandScroll = document.createElement('div');
        expandScroll.className = 'alt-table-expandscroll';
        expandScroll.style.width = `${totalWidth}px`;
        expandScroll.style.height = `${totalHeight}px`;

        expandScroll.appendChild(table);
        box.appendChild(expandScroll);

        return box;
    }

    _refreshTableSize($el){
        let totalWidth = $el.offsetWidth;
        let totalHeight = $el.offsetHeight;

        this.setRenderedSize(totalWidth, totalHeight);

        let colsEle = $el.querySelector('.colnum').children;
        let rowsEle = $el.querySelectorAll('.rownum');

        each(colsEle, (td) => {
            let colWidth = td.offsetWidth;
            let index = td.dataset.col;
            this.colsIns.updateAttr(index, {
                size: colWidth
            });
        }, {
            start: 1
        });

        each(rowsEle, (td) => {
            let rowHeight = td.offsetHeight;
            let index = td.dataset.row;
            this.rowsIns.updateAttr(index, {
                size: rowHeight
            });
        }, {
            start: 1
        });
    }

    setRenderedSize(width, height){
        console.log('setRenderedSize', width, height);
        this.rendered = {
            width: width,
            height: height
        }
    }

    calcVisibleArea(firstTime, scrollLeft = 0, scrollTop = 0){
        let boxWidth = this.width;
        let boxHeight = this.height;

        let colsIns = this.colsIns;
        let rowsIns = this.rowsIns;

        let colStart = colsIns.getEndBySize(scrollLeft, 0);
        if(colStart > 0){
            colStart -= 1;
        }
        let colEnd = colsIns.getEndBySize(boxWidth, colStart);
        if(firstTime){
            colEnd *= 2;
        }

        let rowStart = rowsIns.getEndBySize(scrollTop, 0);
        if(rowStart > 1){
            rowStart -= 1;
        }
        let rowEnd = rowsIns.getEndBySize(boxHeight, rowStart);
        if(firstTime){
            rowEnd *= 2;
        }

        return {
            rowStart,
            rowEnd,
            colStart,
            colEnd
        }
    }

    fresh(){
        let scrollLeft = this.mainTable.box.scrollLeft;
        let scrollTop = this.mainTable.box.scrollTop;

        let viewPort = {
            x: scrollLeft,
            y: scrollTop,
            width: this.width,
            height: this.height
        }
        let renderedBox = {
            x: this.lastPosLeft,
            y: this.lastPosTop,
            width: this.rendered.width,
            height: this.rendered.height
        }
        if(!boxContainViewport(viewPort, renderedBox)){
            this.render(false, scrollLeft, scrollTop);
        }

        this.topTable.box.scrollLeft = scrollLeft;
    }

    _fresh(scrollLeft, scrollTop){
        let visibleArea = this.calcVisibleArea(firstTime, scrollLeft, scrollTop);
        let rowStart = visibleArea.rowStart;
        let rowEnd = visibleArea.rowEnd;
        let colStart = visibleArea.colStart;
        let colEnd = visibleArea.colEnd;
        console.log('render', scrollLeft, scrollTop, rowStart, rowEnd, colStart, colEnd);
        let dataset = this.tableData.getRange({
            row: rowStart,
            col: colStart
        }, {
            row: rowEnd,
            col: colEnd
        });
        let cols = this.colsIns.getData(colStart, colEnd);
        let rows = this.rowsIns.getData(rowStart, rowEnd);

        let tds = this.coreTable.createTds(dataset);
        
        this.coreTable.patchDom({
            tds: tds,
            cols: cols,
            rows: rows,
            rowStart: rowStart,
            rowEnd: rowEnd,
            colStart: colStart,
            colEnd: colEnd
        }, this.lastRenderedInfo)

        let left = this.colsIns.getRangeSize(0, colStart - 1);
        let top = this.rowsIns.getRangeSize(0, rowStart - 1);
        this.setTableTransform(tableDom, left, top);

        tableBox = this.mainTable.box;
        tableBox.querySelector('.alt-table-expandscroll').replaceChild(
            tableDom,
            this.mainTable.table
        );
        
        this._refreshTableSize(tableDom);

        let colgroup = this.coreTable._createColGroup(cols, 'fixed');
        tableDom.replaceChild(colgroup, tableDom.querySelector('colgroup'));
        removeClass(tableDom, 'alt-auto');
        addClass(tableDom, 'alt-fixed');

        if(this.fixedRowTop){
            this.addFixedRowTopTable(firstTime, colStart, colEnd, cols);
        }
    }

    addEvents(){
        let fresh = this.fresh.bind(this);
        this.mainTable.box.addEventListener('scroll', fresh);
        let syncScroll = (function(){
            this.mainTable.box.scrollLeft = this.topTable.box.scrollLeft;
        }).bind(this);
        this.topTable.box.addEventListener('scroll', syncScroll);
    }
}

function boxContainViewport(viewPort, box){
    let vw = viewPort.width;
    let vh = viewPort.height;
    let vx = viewPort.x;
    let vy = viewPort.y;
    let vmx = vx + vw / 2;
    let vmy = vy + vh / 2;

    let bw = box.width;
    let bh = box.height;
    let bx = box.x;
    let by = box.y;
    let bmx = bx + bw / 2;
    let bmy = by + bh / 2;

    // console.log(viewPort, box);
    if(vw < bw && vh < bh && Math.abs(vmx - bmx) <= (bw - vw) / 2 && Math.abs(vmy - bmy) <= (bh - vh) / 2){
        return true;
    }
    return false;
}

export default Table;